package com.zhao.dota.parser;

import com.zhao.dota.model.Performance;
import lombok.Getter;
import skadistats.clarity.io.Util;
import skadistats.clarity.model.Entity;
import skadistats.clarity.model.FieldPath;
import skadistats.clarity.processor.entities.Entities;
import skadistats.clarity.processor.entities.UsesEntities;
import skadistats.clarity.processor.runner.ControllableRunner;
import skadistats.clarity.source.MappedFileSource;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

@UsesEntities
public class PerformanceParser {

    private final ControllableRunner runner;
    @Getter
    private List<Performance> performances;

    public PerformanceParser(File file) throws IOException {
        runner = new ControllableRunner(new MappedFileSource(file));
    }

    private void showScoreboard() {
        showTableWithColumns(
                new Resolver<>("PlayerResource", "m_vecPlayerData.%i.m_iPlayerTeam", null),
                new Resolver<Integer>("PlayerResource", "m_vecPlayerTeamData.%i.m_bHasRandomed", "hasRandomed"), // 是否随机
                new Resolver<Integer>("PlayerResource", "m_vecPlayerTeamData.%i.m_iFirstBloodClaimed", "firstBloodClaimed"), // 拿一血
                new Resolver<Integer>("PlayerResource", "m_vecPlayerTeamData.%i.m_iFirstBloodGiven", "firstBloodGiven"), // 送一血
                new Resolver<Integer>("PlayerResource", "m_vecPlayerTeamData.%i.m_iLevel", "level"),
                new Resolver<Integer>("PlayerResource", "m_vecPlayerTeamData.%i.m_iKills", "kills"),
                new Resolver<Integer>("PlayerResource", "m_vecPlayerTeamData.%i.m_iDeaths", "deaths"),
                new Resolver<Integer>("PlayerResource", "m_vecPlayerTeamData.%i.m_iAssists", "assists"),
                new Resolver<String>("PlayerResource", "m_vecPlayerData.%i.m_iszPlayerName", "playerName"),
                new Resolver<Long>("PlayerResource", "m_vecPlayerData.%i.m_iPlayerSteamID", "steamId"),
                new Resolver<Integer>("Data%n", "m_vecDataTeam.%p.m_iGoldSpentOnBuybacks", "goldSpentOnBuybacks"), //买活花费
                new Resolver<Integer>("Data%n", "m_vecDataTeam.%p.m_iGoldLostToDeath", "goldLostToDeath"), //死亡掉钱
                new Resolver<Integer>("Data%n", "m_vecDataTeam.%p.m_iGoldSpentOnItems", "goldSpentOnItems"), //买装备花费
                new Resolver<Integer>("Data%n", "m_vecDataTeam.%p.m_iGoldSpentOnConsumables", "goldSpentOnConsumables"), // 消耗物品花费
                new Resolver<Integer>("Data%n", "m_vecDataTeam.%p.m_iWardsDestroyed", "wardsDestroyed"), //摧毁守卫
                new Resolver<Integer>("Data%n", "m_vecDataTeam.%p.m_iWardsPurchased", "wardsPurchased"), //购买守卫
                new Resolver<Integer>("Data%n", "m_vecDataTeam.%p.m_iHeroDamage", "heroDamage"), //英雄伤害
                new Resolver<Integer>("Data%n", "m_vecDataTeam.%p.m_iGoldSpentOnSupport", "goldSpentOnSupport"), //辅助道具花费
                new Resolver<Integer>("Data%n", "m_vecDataTeam.%p.m_iRunePickups", "runePickups"), //捡符
                new Resolver<Integer>("Data%n", "m_vecDataTeam.%p.m_iCreepsStacked", "creepsStacked"), //堆野
                new Resolver<Integer>("Data%n", "m_vecDataTeam.%p.m_fStuns", "stuns"), //控制（眩晕？）时间
                new Resolver<Integer>("Data%n", "m_vecDataTeam.%p.m_fHealing", "healing"), //治疗量
                new Resolver<Integer>("Data%n", "m_vecDataTeam.%p.m_iMissCount", "missCount"), //miss数
                new Resolver<Integer>("Data%n", "m_vecDataTeam.%p.m_iReliableGold", "reliableGold"), // 可靠金钱
                new Resolver<Integer>("Data%n", "m_vecDataTeam.%p.m_iRoshanKills", "roshanKills"), // 击杀肉山
                new Resolver<Integer>("Data%n", "m_vecDataTeam.%p.m_iObserverWardsPlaced", "observerWardsPlaced"), // 插假眼
                new Resolver<Integer>("Data%n", "m_vecDataTeam.%p.m_iSentryWardsPlaced", "sentryWardsPlaced"), // 插真眼
                new Resolver<Integer>("Data%n", "m_vecDataTeam.%p.m_iUnreliableGold", "unreliableGold"), // 不可靠金钱
                new Resolver<Integer>("Data%n", "m_vecDataTeam.%p.m_iTotalEarnedXP", "totalEarnedXp"), // 获得经验
                new Resolver<Integer>("Data%n", "m_vecDataTeam.%p.m_iHeroKillGold", "heroKillGold"), // 英雄击杀金钱奖励
                new Resolver<Integer>("Data%n", "m_vecDataTeam.%p.m_iSharedGold", "sharedGold"), //助攻奖励
                new Resolver<Integer>("Data%n", "m_vecDataTeam.%p.m_iIncomeGold", "incomeGold"), //自增金钱
                new Resolver<Integer>("Data%n", "m_vecDataTeam.%p.m_iCreepKillGold", "creepKillGold"), //野怪金钱
                new Resolver<Integer>("Data%n", "m_vecDataTeam.%p.m_iTowerKills", "towerKills"), //推塔数量
                new Resolver<Integer>("Data%n", "m_vecDataTeam.%p.m_iTotalEarnedGold", "totalEarnedGold"),
                new Resolver<Integer>("Data%n", "m_vecDataTeam.%p.m_iLastHitCount", "lastHitCount"),
                new Resolver<Integer>("Data%n", "m_vecDataTeam.%p.m_iDenyCount", "denyCount")
        );
    }

    public void parse() throws InterruptedException {
        runner.runWith(this);
        runner.seek(runner.getLastTick());
        runner.halt();
        performances = new ArrayList<>();
        showScoreboard();
    }

    private void showTableWithColumns(Resolver<Integer> teamResolver, Resolver... columnDefs) {
        int team = 0;
        int pos = 0;

        Class<Performance> performanceClass = Performance.class;
        for (int idx = 0; idx < 256; idx++) {
            try {
                int newTeam = teamResolver.resolveValue(idx, team, pos);
                if (newTeam != team) {
                    team = newTeam;
                    pos = 0;
                } else {
                    pos++;
                }
            } catch (Exception e) {
                // when the team resolver throws an exception, this was the last orderNum there was
                break;
            }
            if (team != 2 && team != 3) {
                continue;
            }
            Performance performance = new Performance();
            for (Resolver resolver : columnDefs) {
                Object o = resolver.resolveValue(idx, team, pos);
                Field field;
                try {
                    field = performanceClass.getDeclaredField(resolver.fieldName);
                } catch (NoSuchFieldException e) {
                    System.out.println("没找到 = " + resolver.fieldName);
                    continue;
                }
                if (field.getType() != o.getClass()) {
                    System.out.println("类型不对 = " + resolver.fieldName);
                    continue;
                }
                field.setAccessible(true);
                try {
                    field.set(performance, o);
                } catch (IllegalAccessException ignore) {
                }
            }
            performances.add(performance);
        }
    }

    private String getEngineDependentEntityName(String entityName) {
        switch (runner.getEngineType().getId()) {
            case SOURCE1:
                return "DT_DOTA_" + entityName;
            case SOURCE2:
                return "CDOTA_" + entityName;
            default:
                throw new RuntimeException("invalid engine type");
        }
    }

    private String getTeamName(int team) {
        switch (team) {
            case 2:
                return "Radiant";
            case 3:
                return "Dire";
            default:
                return "";
        }
    }

    private Entity getEntity(String entityName) {
        return runner.getContext().getProcessor(Entities.class).getByDtName(getEngineDependentEntityName(entityName));
    }

    private class Resolver<V> {

        private final String entityName;
        private final String pattern;
        private final String fieldName;

        public Resolver(String entityName, String pattern, String fieldName) {
            this.entityName = entityName;
            this.pattern = pattern;
            this.fieldName = fieldName;
        }

        public V resolveValue(int index, int team, int pos) {
            String fieldPathString = pattern
                    .replaceAll("%i", Util.arrayIdxToString(index))
                    .replaceAll("%t", Util.arrayIdxToString(team))
                    .replaceAll("%p", Util.arrayIdxToString(pos));
            String compiledName = entityName.replaceAll("%n", getTeamName(team));
            Entity entity = getEntity(compiledName);
            FieldPath fieldPath = entity.getDtClass().getFieldPathForName(fieldPathString);
            return entity.getPropertyForFieldPath(fieldPath);
        }
    }

}
